package com.blank.shop.security.config;

import com.blank.shop.security.authentication.SmsCodeAuthenticationProcessingFilter;
import com.blank.shop.security.authentication.SmsCodeAuthenticationProvider;
import com.blank.shop.security.filter.ImageValidateCodeFilter;
import com.blank.shop.security.filter.SmsValidateCodeFilter;
import com.blank.shop.security.handler.MyAuthenticationFailureHandler;
import com.blank.shop.security.handler.MyAuthenticationSuccessHandler;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;

import javax.annotation.Resource;

/**
 * <br/>Date 2021/9/9
 * <br/>Time 22:57:06
 *
 * @author _blank
 * @see org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
 */
@EnableConfigurationProperties({BlankShopProperties.class})
@Configuration(proxyBeanMethods = false)
@Order(SecurityProperties.BASIC_AUTH_ORDER)
public class MyDefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {

    @Resource
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
    @Resource
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
    @Resource
    private UserDetailsService myUserDetailsServiceImpl;


    private final BlankShopProperties blankShopProperties;
    private final SmsCodeAuthenticationSecurityConfigurer smsCodeAuthenticationSecurityConfigurer;

    public MyDefaultConfigurerAdapter(
            BlankShopProperties blankShopProperties
            , SmsCodeAuthenticationSecurityConfigurer smsCodeAuthenticationSecurityConfigurer) {
        this.blankShopProperties = blankShopProperties;
        this.smsCodeAuthenticationSecurityConfigurer = smsCodeAuthenticationSecurityConfigurer;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    public PersistentTokenRepository persistentTokenRepository() {
        return new InMemoryTokenRepositoryImpl();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 登录页 html
        String loginPage = "/login-page.html";
        // 登录页 controller
        loginPage = "/authentication/require";

        // 处理登录请求
        final String loginProcessingUrl = "/authentication/form.html";

        // session失效跳转地址
        final String invalidSessionUrl = "/session/invalid";

        // 不需要拦截的路径
        final String[] antPatterns = {
                loginPage
                , invalidSessionUrl
                , this.blankShopProperties.getBrowser().getLoginPage()
                , "/code/*"
                , "/error"
        };

        //@formatter:off
        //@formatter:on
        http
                .addFilterBefore(new ImageValidateCodeFilter(this.myAuthenticationFailureHandler), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(new SmsValidateCodeFilter(this.myAuthenticationFailureHandler), UsernamePasswordAuthenticationFilter.class)

//                .httpBasic()
                .formLogin()
                .loginPage(loginPage)
                .loginProcessingUrl(loginProcessingUrl)
                .successHandler(this.myAuthenticationSuccessHandler)
                .failureHandler(this.myAuthenticationFailureHandler)

                .and()

                .rememberMe()
                .tokenRepository(this.persistentTokenRepository())
                .tokenValiditySeconds((int) this.blankShopProperties.getBrowser().getRememberMe().getSeconds())
                .userDetailsService(this.myUserDetailsServiceImpl)

                .and()

                .sessionManagement()
                .invalidSessionUrl(invalidSessionUrl)
                .maximumSessions(1)
                .maxSessionsPreventsLogin(true)
                .expiredUrl(this.blankShopProperties.getBrowser().getLoginPage())
                .expiredSessionStrategy(http.getSharedObject(SimpleRedirectSessionInformationExpiredStrategy.class))
                .and()

                .and()

                .logout()
                .logoutUrl("/sign-out")
                .logoutSuccessUrl("/logout-my.html")
//                .logoutSuccessHandler()
//                .deleteCookies()

                .and()

                .authorizeRequests()
                .antMatchers(antPatterns).permitAll()
                .anyRequest()
                .authenticated()

                .and()

                .csrf()
                .disable()

                .apply(this.smsCodeAuthenticationSecurityConfigurer)
        ;
    }

    @Configuration
    public static class SmsCodeAuthenticationSecurityConfigurer
//            extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>
            extends AbstractHttpConfigurer<SmsCodeAuthenticationSecurityConfigurer, HttpSecurity> {

        @Resource
        private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
        @Resource
        private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
        @Resource
        private UserDetailsService myUserDetailsServiceImpl;

        @Override
        public void configure(HttpSecurity http) throws Exception {
            final SmsCodeAuthenticationProcessingFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationProcessingFilter();
            smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
            smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(this.myAuthenticationSuccessHandler);
            smsCodeAuthenticationFilter.setAuthenticationFailureHandler(this.myAuthenticationFailureHandler);

            final SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
            smsCodeAuthenticationProvider.setUserDetailsService(this.myUserDetailsServiceImpl);

            http.authenticationProvider(smsCodeAuthenticationProvider)
                    .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

            // fixme rememberMe未生效 20210910115720
        }

    }

}
